﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.ServiceModel;
using System.Windows;
using System.Windows.Threading;
using Kopytko.Properties;
using Kopytko.Service;
using NLog;

namespace Kopytko
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        /// <summary>
        /// Globalny logger
        /// </summary>
        public static Logger Log = LogManager.GetCurrentClassLogger();

        /// <summary>
        /// Referencja do aktualnej instancji aplikacji.
        /// </summary>
        public new static App Current
        {
            get { return Application.Current as App; }
        }


        public event EventHandler<ConnectionErrorEventArgs> ConnectionError;
        public event EventHandler ConnectionOpened;


        /// <summary>
        /// Proxy do głównej usługi.
        /// </summary>
        public Service.IMainService ProxyMain { get; private set; }

        /// <summary>
        /// Identyfikator sesji. Nadawany przez usługę po wywołaniu
        /// operacji LogIn. Operacje wymagają podania
        /// tego jako argument.
        /// </summary>
        public int? SessionId { get; set; }

        private void Application_Startup(object sender, StartupEventArgs e)
        {
        }

        private void Application_Exit(object sender, ExitEventArgs e)
        {
            Log.Info("Application Exit");

            CloseMainProxy();
        }

        private void Application_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
        {
            if (SessionId.HasValue)
            {
                Log.FatalException("Unhandled exception [SessId " + SessionId + "]", e.Exception);
            }
            else
            {
                Log.FatalException("Unhandled exception", e.Exception);
            }

            CloseMainProxy();
        }

        public void InitMainProxy()
        {
            Log.Info("InitMainProxy");

            var tmp = new MainServiceClient();

            if (ProxyMain != null)
            {
                try
                {
                    ((MainServiceClient)ProxyMain).Abort();
                }
                catch
                {
                }
            }
            ProxyMain = tmp;
            tmp.InnerChannel.Opened += ProxyMainInnerChannel_Opened;

            try
            {
                tmp.Open();
            }
            catch (Exception ex)
            {
                Log.ErrorException("Proxy Open failed", ex);

                if (ConnectionError != null)
                {
                    ConnectionError(this, new ConnectionErrorEventArgs(ConnectionType.MainService, ex));
                }

                return;
            }
            tmp.InnerChannel.Faulted += ProxyMainInnerChannel_Faulted;
        }

        public void CloseMainProxy()
        {
            Log.Info("CloseMainProxy");

            try
            {
                if (((MainServiceClient)ProxyMain).State == CommunicationState.Opened)
                {
                    ((MainServiceClient)ProxyMain).Close();
                }
                else if (((MainServiceClient)ProxyMain).State == CommunicationState.Faulted)
                {
                    ((MainServiceClient)ProxyMain).Abort();
                }
            }
            catch (Exception)
            {
            }
        }

        private void ProxyMainInnerChannel_Opened(object sender, EventArgs e)
        {
            ((MainServiceClient)ProxyMain).InnerChannel.Opened -= ProxyMainInnerChannel_Opened;

            Log.Info("Proxy opened");

            //#if LOGOWANIE
//            LogIn();
//#else
//            SessionId = 0;
//#endif

            if (ConnectionOpened != null)
            {
                ConnectionOpened(this, new EventArgs());
            }
        }

        private void ProxyMainInnerChannel_Faulted(object sender, EventArgs e)
        {
            Log.Warn("Proxy faulted");

            if (ConnectionError != null)
            {
                ConnectionError(this,
                                new ConnectionErrorEventArgs(ConnectionType.MainService,
                                                             new CommunicationException(
                                                                 "Połączenie zostało przerwane.")));
            }

            try
            {
                ((MainServiceClient)ProxyMain).Abort();
            }
            catch
            {
            }

        }

        public void NotifyConnectionError(object source, ConnectionType type, Exception ex)
        {
            if (ex is FaultException<AuthenticationFault>)
            {
                var auth_fault_ex = (FaultException<AuthenticationFault>)ex;

                if (auth_fault_ex.Detail.Reason == AuthenticationFaultReason.InvalidCredentials)
                {
                }

                Log.ErrorException("AuthenticationFault, reason: " + auth_fault_ex.Detail.Reason, auth_fault_ex);
                return;
            }
            else if (ex is FaultException<ExceptionDetail>)
            {
                // Jeśli na serwerze jest włączone IncludeExceptionDetailInFaults, to tutaj będą szczegóły
                // niezłapanych wyjątków w usłudze.
                var ex2 = ex as FaultException<ExceptionDetail>;
                Log.Error(ex2.Detail + "\r\n" + ex2.StackTrace);
            }
            else
            {
                Log.ErrorException("ConnectionError, type: " + type, ex);
            }

            if (ConnectionError != null)
            {
                ConnectionError(source, new ConnectionErrorEventArgs(type, ex));
            }
        }
    }

    public enum ConnectionType
    {
        Unknown,
        MainService,
    }

    public class ConnectionErrorEventArgs : EventArgs
    {
        public ConnectionErrorEventArgs(ConnectionType type, Exception ex)
        {
            Type = type;
            Exception = ex;
        }

        public ConnectionType Type { get; private set; }
        public Exception Exception { get; private set; }
    }
}
